//Copyright (C) 2000-2006 Gnanasekaran Swaminathan <sekar@sekar.net>
//
//Permission is granted to use at your own risk and distribute this software
//in source and  binary forms provided  the above copyright notice and  this
//paragraph are  preserved on all copies.  This software is provided "as is"
//with no express or implied warranty.
#ifndef SOCKLIB_H
#define SOCKLIB_H

#include <cstdio>
#include <cstring>
#include <cstdarg>
#include <ctime>

#include <exception>
#include <string>
#include <vector>

namespace socklib {
  /// format codes:
  //    $e in fmt string is replaced by strerror(errno)
  //    $m in fmt string is replaced by System Message
  //    $T in fmt string is replaced by YYYY-mm-dd HH:MM:SS time string
  //    $H in fmt string is replaced by hostname
  //    $s in fmt string is replaced with last short socket error
  //    $S in fmt string is replaced with last detailed socket error
  //    $$ in fmt string is replaced by $
  //    % format codes are treated same as printf codes
  struct stringf: std::string {
    stringf(): std::string("") {}

    void vsetf(const char* fmt, va_list ap);
    void vaddf(const char* fmt, va_list ap);

    void setf(const char* fmt, ...);
    void addf(const char* fmt, ...);
  };

  struct exception: std::exception {
    stringf errmsg;

    exception() {}
    exception(const char* fmt, ...);

    void operator()(const char* fmt, ...);
    void seterrf(const char* fmt, ...);
    void vseterrf(const char* fmt, va_list ap);

    const char* what() const { return errmsg.c_str(); }
  };

  struct bytes: std::vector<unsigned char> {
    void add(const char* v);
    void addf(const char* fmt, ...);
    void ends() { insert(end(), 1, 0); }

    std::string get_string() const { return std::string((const char*)&*begin(), size()); }

    bool operator==(const bytes& b) const {
      return size() == b.size() && memcmp(&*begin(), &*b.begin(), size()) == 0;
    }
  };

  struct tmpbuffer {
    const int size;
    char*     buffer;

    tmpbuffer(int sz): size(sz), buffer(new char[size]) {}
    ~tmpbuffer() { delete [] buffer; }
  };

  struct connection {
    virtual ~connection();
    virtual void   write(const void* buf, size_t len) = 0;
    virtual size_t read(void* buf, size_t maxlen) = 0;
  };

  struct file_connection: connection {
    int fd;
    file_connection(const char* filename);
    virtual ~file_connection();

    virtual void   write(const void* buf, size_t len);
    virtual size_t read(void* buf, size_t maxlen);
  };

  struct buffer_connection: connection {
    bytes buffer;

    virtual void   write(const void* buf, size_t len);
    virtual size_t read(void* buf, size_t maxlen);
  };

  struct connection_buffer {
    connection* con;
    unsigned char sbuf[2048];
    int           sbufbeg;
    int           sbufend;
    unsigned char rbuf[2048];
    int           rbufbeg;
    int           rbufend;

    connection_buffer(): con(0), sbufbeg(0), sbufend(0), rbufbeg(0), rbufend(0) {}
    connection_buffer(connection* c): con(c), sbufbeg(0), sbufend(0), rbufbeg(0), rbufend(0) {}
    virtual ~connection_buffer();

    void put_int8(char v);
    void put_uint8(unsigned char v);
    void put_int16(short v);
    void put_uint16(unsigned short v);
    void put_int32(int v);
    void put_uint32(unsigned int v);
    void put_int24(int v);
    void put_uint24(unsigned int v);
    void put_int64(__int64 v);
    void put_uint64(unsigned __int64 v);
    void put_uint48(unsigned __int64 v);
    void put_str(const char* v) { put_buf(v, ::strlen(v)); }
    void put(const std::string& v) { put_buf(v.c_str(), v.size()); }
    void put_byte(unsigned __int8 v) { put_buf(&v, 1); }
    void put_bytes(const bytes& v) { put_buf(&*v.begin(), v.size()); }
    void put_buf(const void* v, size_t len);
    void putf(const char* fmt, ...);
    void flush();
    
    void             unget(); // unget exactly one char
    void             trash(size_t exactlen); // trash exactlen bytes
    unsigned char    get_byte();
    char             get_int8();
    unsigned char    get_uint8();
    short            get_int16();
    unsigned short   get_uint16();
    int              get_int24();
    unsigned int     get_uint24();
    int              get_int32();
    unsigned int     get_uint32();
    __int64          get_int64();
    unsigned __int64 get_uint64();
    unsigned __int64 get_uint48();
    void             get_str(char* v, size_t exactlen) { get_buf(v, exactlen); v[exactlen] = 0; }
    void             get_string(std::string& v, size_t exactlen);
    void             get_line(char* v, size_t maxlen); // get upto and including CRLF; terminate v with 0;
    void             get_line(std::string& v); // get upto and including CRLF
    void             get_buf(void* v, size_t exactlen);
  };

  // some misc wrappers used by the library itself
  extern std::string get_current_image_name();
  extern void        delete_directory(const char* directory);
  extern std::string fmtcomma(int val); // eg: returns -27,567 for -27567
  extern std::string fmtcomma(unsigned int val); // eg: returns 27,567
  extern std::string time2string(time_t t=0); // returns time in Internet standard format
  extern int         duration2seconds(const char* duration);
  extern std::string seconds2duration(int secs);
  extern void        trim(char* str);
  extern void        trim(std::string& str);

  extern void        create_uuid(unsigned char uuid[16]);
  extern std::string uuid2string(const unsigned char uuid[16]);
}

#endif // SOCKLIB_H